import { Object3D, Raycaster, Vector3 } from "three";
import Pixel from "../renderExtension/types/Pixel";
import { App } from "../application";
import GeoUtils from "../utils/GeoUtils";
import { Point } from "../renderExtension/types/Point";
import Const from "../application/constants/Const";
import { MapCarpet } from "../component/models/MapCarpet";
import Helper from "../utils/Helper";
import { TransformControls, TransformControlsGizmo, TransformControlsPlane } from "three/examples/jsm/controls/TransformControls";

export default class Picker {
    private layerArray: Object3D[] = [];
    private static instance: Picker;
   
    
    public static getInstance() {
        if (!Picker.instance) {
            Picker.instance = new Picker();
        }
        return Picker.instance;
    }

    private constructor() { }


    /**
     * 获取图层射线交点
     * @param data 射线拾取需要的参数
     * @param data.pixel 当前鼠标在屏幕上的像素坐标
     * @param data.optionObjects 参与射线拾取的对象数组，如果该参数不传，则对全场景进行射线拾取
     * @param data.isIntersectCarpet 是否对虚拟地毯图层进行射线拾取，如果该参数不传或者为false，则对全场景或者指定的图层进行射线拾取
     * @returns 
     */
    public getPickPoint(data: {pixel: Pixel, optionObjects?: Object3D[], isIntersectCarpet?:boolean}) {
        let camera = App.getInstance().three.camera;
        let raycaster: Raycaster = new Raycaster();
        raycaster.camera = camera;
        let nearNdcPoint = new Vector3();
        let size = App.getInstance().getSize();
        nearNdcPoint.x = (data.pixel.getX() / size.getWidth()) * 2 - 1;
        nearNdcPoint.y = -(data.pixel.getY() / size.getHeight()) * 2 + 1;
        nearNdcPoint.z = -1;
        let nearPoint = nearNdcPoint.clone().unproject(camera);
    
        let farNdcPoint = new Vector3(nearNdcPoint.x, nearNdcPoint.y, 1);
        let farPoint = farNdcPoint.clone().unproject(camera);
            
        let rayDirection = farPoint.sub(nearPoint).normalize();
        raycaster.set(nearPoint, rayDirection);

        let app = App.getInstance();
        let scene = app.three.scene;
        let objs;
        if(data.isIntersectCarpet == false){ //对全场景或者指定的图层进行射线拾取
            this.layerArray = [];
            if(data.optionObjects && data.optionObjects.length > 0){
                this.layerArray = data.optionObjects;
            }else{
                Helper.traverseObject(scene, this.selectCallback.bind(this));
            }
            objs = raycaster.intersectObjects(this.layerArray, true);
        }else{ //对虚拟地毯图层进行射线拾取
            let mapCarpet = app.map.mapVirtualCarpet;
            objs = raycaster.intersectObject(mapCarpet, true);
        }
        return this.generateResultData(objs, data.pixel);
    }

    private selectCallback(obj){
        if (obj.canNotSelect) { //true表示该图层不参与射线拾取，false表示图层参与射线拾取
            return false;
        }
        if((obj instanceof TransformControls || obj instanceof TransformControlsGizmo || obj instanceof TransformControlsPlane)){ //控制器不参与射线拾取
            return false;
        }
        if (obj.isMesh || obj.isGroup || obj.isSprite){
            this.layerArray.push(obj);
            return false;
        }
        
        return true;
    }

    private generateResultData(objs, pixel) {
        if (objs.length === 0) {
            let lnglat = GeoUtils.containerToLngLat(pixel);
            return { lnglat:lnglat, intersectObj: null, carpetLnglat: lnglat };
        } else {
            objs.sort((a, b) => {
                if (a.object['pickOrder'] == null || a.object['pickOrder'] == undefined) {
                    a.object['pickOrder'] = Const.PickIndex.defaultIndex;
                };
                 if (b.object['pickOrder'] == null || b.object['pickOrder'] == undefined) {
                    a.object['pickOrder'] = Const.PickIndex.defaultIndex;
                };
                return b.object['pickOrder'] - a.object['pickOrder'];
            });

            let pickObj = objs[0];
            if(objs.length > 1 && pickObj.object instanceof MapCarpet){ //MapCarpet是虚拟的底图，如果有其他的被拾取物体，选择其他的物体
                pickObj = objs[1];
            }
            let point = pickObj.point;

            point.x = point.x % Const.MapConst.tileWidth;
            point.y = point.y % Const.MapConst.tileWidth;
            
            let lnglat = new Point(point.x, point.y, point.z, Const.EPSGType.EPSGWeb).toGeography();

            let carpetLnglat;
            for(let i=0; i<objs.length; i++){
                let curObj = objs[i].object;
                let curPoint = objs[i].point;
                if(curObj instanceof MapCarpet){
                    curPoint.x = curPoint.x % Const.MapConst.tileWidth;
                    curPoint.y = curPoint.y % Const.MapConst.tileWidth;
                    carpetLnglat = new Point(curPoint.x, curPoint.y, curPoint.z, Const.EPSGType.EPSGWeb).toGeography();
                }
            }
            if(!carpetLnglat){
                carpetLnglat = lnglat.clone();
            }
            return { lnglat: lnglat, intersectObj: pickObj, carpetLnglat: carpetLnglat };
        }
    }
}